#include "FITKOCCModelClinching.h"
#include <gp_Ax2.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <Geom_Circle.hxx>
#include <gp_Circ.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <TopExp.hxx>
#include <BRep_Tool.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>


#include <ShapeFix_Wire.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <gp_Pln.hxx>
#include <IntTools_FClass2d.hxx>
#include <Geom_Plane.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <TopoDS.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <ShapeAnalysis.hxx>
#include <ShapeFix_Shape.hxx>

namespace OCC
{
    /**
     * @brief 无铆连接--上模
     */
    FITKOCCModelClinchingPunch::FITKOCCModelClinchingPunch():OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    bool FITKOCCModelClinchingPunch::update()
    {
        //求解p0,p1,p2,p3,O1坐标
        double location[3] = { 0.0, 0.0, 0.0 };
        gp_Pnt p0(location[0], location[1], location[2]);

        //三角形三条边
        double a = _OutRadius-_innerRadius;double b = _Length;double c = sqrt(a*a+b*b);
        double cosM = (a/c);double sinM = (b/c);
        double arcsinM = asin(sinM);
        double tanm = (1-cosM)/sinM;

        //求l的长度
        double l = _Radius*tanm;

        //圆心及圆起点终点坐标
        gp_Pnt O1(location[0]+_innerRadius-l,location[1],location[2]+_Radius);
        gp_Pnt p1(location[0]+_innerRadius-l,location[1],location[2]);
        gp_Pnt p3(location[0]+_OutRadius,location[1], location[2]+_Length);

        //绘制圆弧（角度方向是时钟方向，最上端为0度，顺时针依次增大）
        gp_Ax2 axis(O1, gp_Dir(0, 1, 0));
        Standard_Real alpha1 = M_PI-arcsinM;
        Standard_Real alpha2 = M_PI;
        Handle(Geom_Circle) circle = new Geom_Circle(axis, _Radius);
        Handle(Geom_TrimmedCurve) Arc = new Geom_TrimmedCurve(circle, alpha1, alpha2);
        BRepBuilderAPI_MakeEdge edgeArc(Arc);
        TopoDS_Vertex FirstVertex = TopExp::FirstVertex(edgeArc.Edge());
        gp_Pnt p2 = BRep_Tool::Pnt(FirstVertex);

        //线段绘制
        TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(p0,p1);
        TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge(p2,p3);
        BRepBuilderAPI_MakeWire wire;
        wire.Add(edge1);
        wire.Add(edgeArc.Edge());
        wire.Add(edge3);
        TopoDS_Shape Punch= BRepPrimAPI_MakeRevol(wire, gp::OZ());

        //创建无铆连接--上模  ++ 异常处理。
        try
        {
            const TopoDS_Shape & output = Punch;
            if (output.IsNull())
            {
                printLog(tr("Invalid parameters !"), 3);
                return false;
            }

            _occShapeAgent->updateShape(Punch);
        }
        catch (...)
        {
            printLog(tr("Invalid parameters !"), 3);
            return false;
        }

        return true;
    }


    /**
     * @brief 无铆连接--压边圈
     */
    FITKOCCModelClinchingBH::FITKOCCModelClinchingBH():OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    bool FITKOCCModelClinchingBH::update()
    {
        //求解压边圈p0,p1点的坐标
        gp_Pnt p0(_point1[0]+_innerRadius,_point1[1], _point1[2]);
        gp_Pnt p1(_point1[0]+_OutRadius,_point1[1], _point1[2]);
        TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(p0,p1);

        BRepBuilderAPI_MakeWire wire;
        wire.Add(edge1);
        TopoDS_Shape BH= BRepPrimAPI_MakeRevol(wire, gp::OZ());

        //创建无铆连接--压边圈  ++ 异常处理。
        try
        {
            const TopoDS_Shape & output = BH;
            if (output.IsNull())
            {
                printLog(tr("Invalid parameters !"), 3);
                return false;
            }

            _occShapeAgent->updateShape(BH);
        }
        catch (...)
        {
            printLog(tr("Invalid parameters !"), 3);
            return false;
        }

        return true;
    }


    /**
     * @brief 无铆连接--下模
     */
    FITKOCCModelClinchingDie::FITKOCCModelClinchingDie():OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    bool FITKOCCModelClinchingDie::update()
    {
        //求解模具p0,p1,p2,p3,p4,p5,O1,O2点的坐标
        gp_Pnt p0(_point1[0], _point1[1], _point1[2]);
        gp_Pnt p5(_point1[0]+_OutRadius,_point1[1], _point1[2]+_Length);
        //因为模具倒角位置均垂直，故直接求得坐标（若不垂直，求解方法见GeoCommandCreateWuMaoPunch）
        gp_Pnt p1(_point1[0]+_innerRadius-_Radius, _point1[1], _point1[2]);
        gp_Pnt p2(_point1[0]+_innerRadius, _point1[1], _point1[2]+_Radius);
        gp_Pnt p3(_point1[0]+_innerRadius, _point1[1], _point1[2]+_Length-_Radius);
        gp_Pnt p4(_point1[0]+_innerRadius+_Radius, _point1[1], _point1[2]+_Length);

        //画圆弧
        gp_Pnt O1(_point1[0]+_innerRadius-_Radius, _point1[1], _point1[2]+_Radius);
        gp_Pnt O2(_point1[0]+_innerRadius+_Radius, _point1[1], _point1[2]+_Length-_Radius);

        gp_Ax2 axis1(O1, gp_Dir(0, 1, 0));
        Handle(Geom_Circle) circle1 = new Geom_Circle(axis1, _Radius);
        Handle(Geom_TrimmedCurve) Arc1 = new Geom_TrimmedCurve(circle1, M_PI/2, M_PI);
        BRepBuilderAPI_MakeEdge edgeArc1(Arc1);

        gp_Ax2 axis2(O2, gp_Dir(0, 1, 0));
        Handle(Geom_Circle) circle2 = new Geom_Circle(axis2, _Radius);
        Handle(Geom_TrimmedCurve) Arc2 = new Geom_TrimmedCurve(circle2, 3*M_PI/2, 2*M_PI);
        BRepBuilderAPI_MakeEdge edgeArc2(Arc2);

        TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(p0,p1);
        TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge(p2,p3);
        TopoDS_Edge edge5 = BRepBuilderAPI_MakeEdge(p4,p5);

        BRepBuilderAPI_MakeWire wire;
        wire.Add(edge1);
        wire.Add(edgeArc1.Edge());
        wire.Add(edge3);
        wire.Add(edgeArc2.Edge());
        wire.Add(edge5);
        TopoDS_Shape Die= BRepPrimAPI_MakeRevol(wire, gp::OZ());

        //创建无铆连接--下模  ++ 异常处理。
        try
        {
            const TopoDS_Shape & output = Die;
            if (output.IsNull())
            {
                printLog(tr("Invalid parameters !"), 3);
                return false;
            }

            _occShapeAgent->updateShape(Die);
        }
        catch (...)
        {
            printLog(tr("Invalid parameters !"), 3);
            return false;
        }

        return true;
    }


    /**
     * @brief 无铆连接--上板料
     */
    FITKOCCModelClinchingTopSheet::FITKOCCModelClinchingTopSheet():OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    bool FITKOCCModelClinchingTopSheet::update()
    {
        gp_Ax2 anAxis;
        double axis[3] = { 0.0, 0.0, 1.0 };
        anAxis.SetLocation(gp_Pnt(_point1[0], _point1[1], _point1[2]));
        anAxis.SetDirection(gp_Dir(axis[0], axis[1], axis[2]));

        TopoDS_Shape TopSheet = BRepPrimAPI_MakeCylinder(anAxis, _Radius, _Length).Shape();

        //创建无铆连接--上板料  ++ 异常处理。
        try
        {
            const TopoDS_Shape & output = TopSheet;
            if (output.IsNull())
            {
                printLog(tr("Invalid parameters !"), 3);
                return false;
            }

            _occShapeAgent->updateShape(TopSheet);
        }
        catch (...)
        {
            printLog(tr("Invalid parameters !"), 3);
            return false;
        }

        return true;
    }


    /**
     * @brief 无铆连接--下板料
     */
    FITKOCCModelClinchingBottomSheet::FITKOCCModelClinchingBottomSheet():OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    bool FITKOCCModelClinchingBottomSheet::update()
    {
        gp_Ax2 anAxis;
        double axis[3] = { 0.0, 0.0, 1.0 };
        anAxis.SetLocation(gp_Pnt(_point1[0], _point1[1], _point1[2]));
        anAxis.SetDirection(gp_Dir(axis[0], axis[1], axis[2]));

        TopoDS_Shape BottomSheet = BRepPrimAPI_MakeCylinder(anAxis, _Radius, _Length).Shape();

        //创建无铆连接--下板料  ++ 异常处理。
        try
        {
            const TopoDS_Shape & output = BottomSheet;
            if (output.IsNull())
            {
                printLog(tr("Invalid parameters !"), 3);
                return false;
            }

            _occShapeAgent->updateShape(BottomSheet);
        }
        catch (...)
        {
            printLog(tr("Invalid parameters !"), 3);
            return false;
        }

        return true;
    }


//    /**
//     * @brief 控制几何
//     */
//    std::vector<TopoDS_Wire> GeoCommandCommon::bulidWire(std::list<TopoDS_Edge> &edgeList)
//    {
//        std::vector<TopoDS_Wire> wires;

//        while (edgeList.size() > 0)
//        {
//            BRepBuilderAPI_MakeWire mkWire;
//            mkWire.Add(edgeList.front());
//            edgeList.pop_front();

//            TopoDS_Wire new_wire = mkWire.Wire(); // current new wire

//            bool found = false;
//            do
//            {
//                found = false;
//                for (std::list<TopoDS_Edge>::iterator pE = edgeList.begin(); pE != edgeList.end(); ++pE)
//                {
//                    mkWire.Add(*pE);
//                    if (mkWire.Error() != BRepBuilderAPI_DisconnectedWire)
//                    {
//                        found = true;
//                        edgeList.erase(pE);
//                        new_wire = mkWire.Wire();
//                        break;
//                    }
//                }
//            } while (found);

//            // Fix any topological issues of the wire
//            ShapeFix_Wire aFix;
//            aFix.SetPrecision(Precision::Confusion());
//            aFix.Load(new_wire);
//            aFix.FixReorder();
//            aFix.FixConnected();
//            aFix.FixClosed();
//            wires.push_back(aFix.Wire());
//        }

//        return wires;
//    }

//    TopoDS_Shape GeoCommandCommon::makeFace(std::vector<TopoDS_Wire> &w)
//    {
//        if (w.empty())
//            return TopoDS_Shape();

//        std::vector<TopoDS_Wire> wires = w;
//        std::sort(wires.begin(), wires.end(), Wire_Compare());
//        std::list<TopoDS_Wire> wire_list;
//        wire_list.insert(wire_list.begin(), wires.rbegin(), wires.rend());

//        // separate the wires into several independent faces
//        std::list<std::list<TopoDS_Wire>> sep_wire_list;
//        while (!wire_list.empty())
//        {
//            std::list<TopoDS_Wire> sep_list;
//            TopoDS_Wire wire = wire_list.front();
//            wire_list.pop_front();
//            sep_list.push_back(wire);

//            std::list<TopoDS_Wire>::iterator it = wire_list.begin();
//            while (it != wire_list.end())
//            {
//                if (isInside(wire, *it))
//                {
//                    sep_list.push_back(*it);
//                    it = wire_list.erase(it);
//                }
//                else
//                {
//                    ++it;
//                }
//            }

//            sep_wire_list.push_back(sep_list);
//        }

//        if (sep_wire_list.size() == 1)
//        {
//            std::list<TopoDS_Wire> &wires = sep_wire_list.front();
//            return makeFace(wires);
//        }
//        else if (sep_wire_list.size() > 1)
//        {
//            TopoDS_Compound comp;
//            BRep_Builder builder;
//            builder.MakeCompound(comp);
//            for (std::list<std::list<TopoDS_Wire>>::iterator it = sep_wire_list.begin(); it != sep_wire_list.end(); ++it)
//            {
//                TopoDS_Shape aFace = makeFace(*it);
//                if (!aFace.IsNull())
//                    builder.Add(comp, aFace);
//            }
//            if (comp.Closed() == false)
//            {
//                return TopoDS_Shape(); // no closed.
//            }
//            return comp;
//        }
//        else
//        {
//            return TopoDS_Shape(); // error
//        }
//    }

//    TopoDS_Shape GeoCommandCommon::makeFace(std::list<TopoDS_Wire> &wires)
//    {
//        if (wires.size() < 1)
//        {
//            return TopoDS_Shape();
//        }
//        BRepBuilderAPI_MakeFace mkFace(wires.front());
//        const TopoDS_Face &face = mkFace.Face();
//        if (face.IsNull())
//            return face;
//        gp_Dir axis(0, 0, 1);
//        BRepAdaptor_Surface adapt(face);
//        if (adapt.GetType() == GeomAbs_Plane)
//        {
//            axis = adapt.Plane().Axis().Direction();
//        }

//        wires.pop_front();
//        for (std::list<TopoDS_Wire>::iterator it = wires.begin(); it != wires.end(); ++it)
//        {
//            BRepBuilderAPI_MakeFace mkInnerFace(*it);
//            const TopoDS_Face &inner_face = mkInnerFace.Face();
//            if (inner_face.IsNull())
//                return inner_face; // failure
//            gp_Dir inner_axis(0, 0, 1);
//            BRepAdaptor_Surface adapt(inner_face);
//            if (adapt.GetType() == GeomAbs_Plane)
//            {
//                inner_axis = adapt.Plane().Axis().Direction();
//            }
//            // It seems that orientation is always 'Forward' and we only have to reverse
//            // if the underlying plane have opposite normals.
//            if (axis.Dot(inner_axis) < 0)
//                it->Reverse();
//            mkFace.Add(*it);
//        }
//        return validateFace(mkFace.Face());
//    }
//    bool GeoCommandCommon::isInside(const TopoDS_Wire &wire1, const TopoDS_Wire &wire2)
//    {
//        Bnd_Box box1;
//        BRepBndLib::Add(wire1, box1);
//        box1.SetGap(0.0);

//        Bnd_Box box2;
//        BRepBndLib::Add(wire2, box2);
//        box2.SetGap(0.0);

//        if (box1.IsOut(box2))
//            return false;

//        double prec = Precision::Confusion();
//        prec = 1E-6;

//        BRepBuilderAPI_MakeFace mkFace(wire1);
//        if (!mkFace.IsDone())
//            Standard_Failure::Raise("Failed to create a face from wire in sketch");
//        TopoDS_Face face = validateFace(mkFace.Face());
//        BRepAdaptor_Surface adapt(face);
//        IntTools_FClass2d class2d(face, prec);
//        Handle_Geom_Surface surf = new Geom_Plane(adapt.Plane());
//        ShapeAnalysis_Surface as(surf);

//        TopExp_Explorer xp(wire2, TopAbs_VERTEX);
//        while (xp.More())
//        {
//            TopoDS_Vertex v = TopoDS::Vertex(xp.Current());
//            gp_Pnt p = BRep_Tool::Pnt(v);
//            gp_Pnt2d uv = as.ValueOfUV(p, prec);
//            if (class2d.Perform(uv) == TopAbs_IN)
//                return true;
//            // TODO: We can make a check to see if all points are inside or all outside
//            // because otherwise we have some intersections which is not allowed
//            else
//                return false;
//            xp.Next();
//        }

//        return false;
//    }
//    TopoDS_Face GeoCommandCommon::validateFace(const TopoDS_Face &face)
//    {
//        BRepCheck_Analyzer aChecker(face);
//        if (!aChecker.IsValid())
//        {
//            TopoDS_Wire outerwire = ShapeAnalysis::OuterWire(face);
//            TopTools_IndexedMapOfShape myMap;
//            myMap.Add(outerwire);

//            TopExp_Explorer xp(face, TopAbs_WIRE);
//            ShapeFix_Wire fix;
//            fix.SetFace(face);
//            fix.Load(outerwire);
//            fix.Perform();
//            BRepBuilderAPI_MakeFace mkFace(fix.WireAPIMake());
//            while (xp.More())
//            {
//                if (!myMap.Contains(xp.Current()))
//                {
//                    fix.Load(TopoDS::Wire(xp.Current()));
//                    fix.Perform();
//                    mkFace.Add(fix.WireAPIMake());
//                }
//                xp.Next();
//            }

//            aChecker.Init(mkFace.Face());
//            if (!aChecker.IsValid())
//            {
//                ShapeFix_Shape fix(mkFace.Face());
//                fix.SetPrecision(Precision::Confusion());
//                fix.SetMaxTolerance(Precision::Confusion());
//                fix.SetMaxTolerance(Precision::Confusion());
//                fix.Perform();
//                fix.FixWireTool()->Perform();
//                fix.FixFaceTool()->Perform();
//                TopoDS_Face fixedFace = TopoDS::Face(fix.Shape());
//                aChecker.Init(fixedFace);
//                if (!aChecker.IsValid())
//                    return TopoDS_Face();
//                // Standard_Failure::Raise("Failed to validate broken face");
//                return fixedFace;
//            }
//            return mkFace.Face();
//        }

//        return face;
//    }

}

